////////////////////////////////////////////////////////////////////////////////
// This file is part of the Mist compiler.                                    //
//                                                                            //
// Copyright (C) 2009 The Mist project                                        //
//                                                                            //
// For a list of contributors, see the COMMITTERS file.                       //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// in the COPYING file. If not, see <http://www.gnu.org/licenses/>.           //
////////////////////////////////////////////////////////////////////////////////

///
/// \file
/// Contains the implementation of the Symbol methods.
///

#include "symbol.hpp"

////////////////////////////////////////////////////////////////////////////////
// Includes                                                                   //
////////////////////////////////////////////////////////////////////////////////

#include <stack>
using std::stack;

#include <set>
using std::set;

#include <boost/algorithm/string/replace.hpp>
using boost::replace_all_copy;

#include "common/string.hpp"
#include "parse.hpp"

////////////////////////////////////////////////////////////////////////////////
// Function Implementations                                                   //
////////////////////////////////////////////////////////////////////////////////

sptr<Symbol> Symbol::construct_root() {
	sptr<Symbol> result(new Symbol);
	
	result->newScope = true;
	result->transparent = false;
	
	result->access = AccessType(false, false, false);
	
	result->root = result;
	
	result->_childCount = 0;
	
	return result;
}

sptr<Symbol> Symbol::construct_function(wptr<Symbol> parent, string name, sptr<FunctionType> type, vector<wptr<Identifier> > templateTypeNames, wptr<FunctionDeclaration> decl) {
	// TODO: Geef een bestaande functie symbool terug als deze er is.
	
	sptr<Symbol> result(new Symbol);
	
	result->newScope = true;
	result->transparent = false;
	
	result->name = name;
	result->type = type;
	result->access = AccessType(true, false, false);
	result->declaration = decl;
	
	result->root = parent->root;
	result->parent = parent;
	
	result->parent->insert_child(name, TypeSpec(templateTypeNames, weak_vector(type->parameterTypes)), result);
	
	result->_myCountInParent = ++(result->parent->_childCount);
	
	result->_childCount = 0;
	
	return result;
}

sptr<Symbol> Symbol::construct_variable(wptr<Symbol> parent, string name, sptr<Type> type) {
	sptr<Symbol> result(new Symbol);
	
	result->newScope = false;
	result->transparent = false;
	
	result->name = name;
	result->type = type;
	result->access = AccessType(true, true, false);
	
	result->root = parent->root;
	result->parent = parent;
	
	result->parent->insert_child(name, TypeSpec(), result);
	
	result->_myCountInParent = ++(result->parent->_childCount);
	
	result->_childCount = 0;
	
	return result;
}

sptr<Symbol> Symbol::construct_iterator(wptr<Symbol> parent, string name, sptr<Type> type) {
	sptr<Symbol> result(new Symbol);
	
	result->newScope = false;
	result->transparent = false;
	
	result->name = name;
	result->type = type;
	result->access = AccessType(true, false, false);
	
	result->root = parent->root;
	result->parent = parent;
	
	result->parent->insert_child(name, TypeSpec(), result);
	
	result->_myCountInParent = ++(result->parent->_childCount);
	
	result->_childCount = 0;
	
	return result;
}

sptr<Symbol> Symbol::construct_logical_variable(wptr<Symbol> parent, string name, sptr<Type> type) {
	sptr<Symbol> result(new Symbol);
	
	result->newScope = false;
	result->transparent = false;
	
	result->name = name;
	result->type = type;
	result->access = AccessType(true, false, true);
	
	result->root = parent->root;
	result->parent = parent;
	
	result->parent->insert_child(name, TypeSpec(), result);
	
	result->_myCountInParent = ++(result->parent->_childCount);
	
	result->_childCount = 0;
	
	return result;
}

sptr<Symbol> Symbol::construct_blinder(wptr<Symbol> parent) {
	sptr<Symbol> result(new Symbol);
	
	result->newScope = false;
	result->transparent = false;
	
	result->access = AccessType(false, false, false);
	
	result->root = parent->root;
	result->parent = parent;
	
	result->parent->insert_child(result);
	
	result->_myCountInParent = ++(result->parent->_childCount);
	
	result->_childCount = 0;
	
	return result;
}

sptr<Symbol> Symbol::construct_type(wptr<Symbol> parent, string name, wptr<TypeDeclaration> decl) {
	sptr<Symbol> result(new Symbol);
	
	result->newScope = false;
	result->transparent = false;
	
	result->name = name;
	result->type = TypeType::construct();
	result->type->scope = parent;
	result->access = AccessType(true, false, false);
	result->typeDeclaration = decl;
	
	result->root = parent->root;
	result->parent = parent;
	
	result->parent->insert_child(name, TypeSpec(), result);
	
	result->_myCountInParent = ++(result->parent->_childCount);
	
	result->_childCount = 0;
	
	return result;
}

sptr<Symbol> Symbol::construct_scope(wptr<Symbol> parent, optional<string> name) {
	sptr<Symbol> result(new Symbol);
	
	result->newScope = true;
	result->transparent = false;
	
	result->name = name;
	result->access = AccessType(false, false, false);
	
	result->root = parent->root;
	result->parent = parent;
	
	if (name)
		result->parent->insert_child(*name, TypeSpec(), result);
	else
		result->parent->insert_child(result);
	
	result->_myCountInParent = ++(result->parent->_childCount);
	
	result->_childCount = 0;
	
	return result;
}

void Symbol::insert_child(sptr<Symbol> symbol) {
	children.insert(symbol);
}

void Symbol::insert_child(const string& name, const TypeSpec& typeSpec, sptr<Symbol> symbol) {
	insert_child(symbol);
	typedef multimap<TypeSpec, wptr<Symbol>, TypeSpec::Before>::value_type VT;
	VT child = VT(typeSpec, symbol);
	childrenByName[name].insert(child);
	symbol->typeSpec = typeSpec;
}

string Symbol::unique_name() {
	string result;
	
	for (wptr<Symbol> symbol = wptr_this(); symbol->parent; symbol = symbol->parent) {
		string part;
		
		if (symbol->name) {
			part += replace_all_copy(*symbol->name, "_", "__");
			
			if (symbol->typeSpec)
				part += "_O" + to_string(symbol->_myCountInParent) + "_C";
		} else {
			part += "_O" + to_string(symbol->_myCountInParent) + "_C";
		}
		
		result = "_D" + part + result;
	}
	
	return result;
}

sptr<Reference> Symbol::create_absolute_reference(wptr<Symbol> scope) {
	vector<string> names;
	
	for (wptr<Symbol> symbol = wptr_this(); symbol; symbol = symbol->parent) {
		if (!symbol->name && symbol->compoundStatement && typeid(*symbol->compoundStatement->parent) != typeid(FunctionDeclaration)) {
			string newName = new_unique_identifier(root, "scope");
			symbol->name = newName;
			symbol->parent->insert_child(newName, TypeSpec(), symbol);
			symbol->compoundStatement->name = Identifier::construct(newName);
			symbol->compoundStatement->name->scope.set_automatically(symbol->compoundStatement->scope());
			symbol->compoundStatement->name->parent = symbol->compoundStatement;
		}
		
		if (symbol->name)
			names.insert(names.begin(), *symbol->name);
	}
	
	names.insert(names.begin(), "");
	
	return parse<Reference>(implode(names, "."), scope);
}

////////////////////////////////////////////////////////////////////////////////

void find_symbol_names(wptr<Symbol> from, set<string>& names) {
	if (from->name) 
		names.insert(*from->name);
	foreach (wptr<Symbol> child, from->children)
		find_symbol_names(child, names);
}

string new_unique_identifier(wptr<Symbol> from, string prefix) {
	set<string> namedSymbols;
	find_symbol_names(from, namedSymbols);
	
	uint index = 0;
	string result;
	do {
		result = prefix + "_" + to_string(++index);
	} while (namedSymbols.count(result));
	
	return result;
}
